/*
 * downloader.h
 *
 *  Created on: 2023年6月13日
 *      Author: pengzc
 */

#ifndef HTTP_DOWNLOADER_H_
#define HTTP_DOWNLOADER_H_

#include <string>
#include <functional>
#include <list>
#include <system/Thread.h>
#include <mutex>
#include <set>
#include "http_client.h"

/**
 * 下载器实现的功能
 * 可添加一个或者多个任务
 * 监听任务进度
 * 下载完成通知
 */

namespace http {

class Downloader : protected Thread {
public:
  struct Task;
  /**
   * return true 继续下载
   *        false 取消下载
   */
  typedef std::function<bool(const Task& task)> DownloadBeforeCallback;
  typedef std::function<void(const Task& task, int64_t dl, int64_t total)> DownloadProgressCallback;
  typedef std::function<void(const Task& task, bool success)> DownloadResultCallback;

  typedef void (*TaskListener)(const Task& task, int64_t dl, int64_t total);

  struct Task {
    std::string source;//下载链接
    std::string target;//另存为
    int retry_max = 3; //最大重试次数
    SpeedLimit low_speed_limit; //低网速超时
    DownloadBeforeCallback before; //任务开始之前
    DownloadProgressCallback progress; //下载进度
    DownloadResultCallback result; //下载结果
  };

  Downloader();
  virtual ~Downloader();

  static Downloader& instance();

  void add(const Task& task);

  /**
   * 剩余任务数量
   * @return
   */
  int size();

  void addListener(const TaskListener listener);
  void removeListener(const TaskListener listener);


  Downloader& operator=(Downloader&) = delete;
  Downloader(Downloader&) = delete;

  /**
   * @brief 使用GET方法下载，如果状态码不是200或206，则视为失败，
   *        如果实际长度与响应头中的Content-Length不同，也视为失败
   *        失败时将抛出异常。
   * @param source  源地址
   * @param target  保存路径
   * @param retry 重试次数
   * @param low_flow_limit 低速限制，如果低于这个速度，则认为超时
   * @param progress 进度监听
   */
  static void download(
      const std::string& source,
      const std::string& target,
      int retry,
      const SpeedLimit& low_flow_limit,
      const http::HTTPClient::ProgressCallback& progress);

protected:
  virtual bool threadLoop();
  bool download(const Task& task);

protected:
  std::recursive_mutex list_mutex_;
  std::list<Task> task_list_;
  std::set<TaskListener> listener_set_;
};

} /* namespace http */

#endif /* HTTP_DOWNLOADER_H_ */
